<?php
//补贴相关的微信服务

namespace App\Services\Wx;

use App\Models\WxActionLogModel;
use App\Services\ReceiverService;
use App\Services\SubsidyBillService;
use Carbon\Carbon;

class SubsidyWxService
{

    /**
     * 创建补贴单据
     * @param $receiverId
     * @param $openid
     * @param $userName
     * @param $transferAmount
     * @param $transferRemark
     * @param $continueBillInfo  //继续上次创建已单据的信息

     * @return array
     */
    public static function createSubsidyBill(
        $activityId,
        $receiverId,
        $openid,
        $userName,
        $transferAmount = 0,
        $transferRemark = '',
        $continueBillInfo = []
    ) {
        //请求创建单据api
        $wxTransfer = new WxTransfer();
        //上次转账未完成，重新查询单据状态
        if ($continueBillInfo && in_array($continueBillInfo['status'], [1, 2])) {
            $outBillNo = $continueBillInfo['out_bill_no'];
            $packageInfo = $continueBillInfo['package_info'];
            $billId = $continueBillInfo['id'];
        } else {
            //商户单号 商户系统内部的商家单号，要求此参数只能由数字、大小写字母组成，在商户系统内部唯一
            $randNum = rand(0000, 9999);
            $outSuffix = sprintf("%04d", $randNum);
            $outBillNo = 'subsidy'.date('YmdHis').$receiverId.$outSuffix; //本次发放 商户单据号，需保证唯一

            //todo: 补贴活动场景ID，1012为测试用，正式使用1013
            $transferSceneId = 1012; //转账场景ID 该批次转账使用的转账场景，可前往“商家转账到零钱-前往功能”中申请。如：1001-现金营销
            //申请创建转账单据
            $createRes = $wxTransfer->createTransferBill($openid, $outBillNo, $transferSceneId, $userName,
                $transferAmount, $transferRemark);
            //记录微信动作日志
            $saveData = [
                'event'    => 'coupon_consume',
                'event_id' => $outBillNo,
                'desc'     => "领取补贴，创建单据",
                'data'     => json_encode([
                    'query' => [
                        'activity_id' => $activityId,
                        'receiver_id' => $receiverId,
                        'openid' => $openid,
                        'user_name' => $userName,
                        'transfer_amount' => $transferAmount,
                        'transfer_remark' => $transferRemark,
                        'continue_bill_info' => $continueBillInfo
                    ],
                    'request_message' => $createRes,
                ]),
                'openid' => $openid,
                'activity_id' => $activityId,
                'status'   => 1,
            ];
            WxActionLogModel::query()
                ->insert($saveData);

            if (!$createRes || $createRes['state'] == 'FAIL') {
                return resultError('创建转账单据失败');
            }
            //保存单据信息
            $saveBillRes = SubsidyBillService::save([
                'activity_id' => $activityId, //活动id
                'receiver_id' => $receiverId, //领券人id
                'openid' => $openid, //微信id
                'out_bill_no' => $outBillNo, //商户单号
                'transfer_scene_id' => $transferSceneId, //转账场景ID
                'transfer_remark' => $transferRemark, //转账备注
                'transfer_bill_no' => $createRes['transfer_bill_no'], //微信转账单号
                'package_info' => $createRes['package_info'], //商家转账付款单跳转微信支付收款页的package信息
                'status' => SubsidyBillService::convertWxStateToSubsidyBillStatus($createRes['state']), //单据处理状态：1. 单据处理中；2.待用户确认；3.转账处理中；4.转账成功；5.转账失败；
                'fail_reason' => '', //失败原因
            ]);
            if($saveBillRes['errcode']) {
                return resultError('创建转账单据失败');
            }
            $packageInfo = $createRes['package_info'];
            $billId = $saveBillRes['data']['id'];
        }
        //创建单据成功，阻塞进程，等待单据审核完成，状态置为待用户确认，最大阻塞时间为3s
        $maxWaitSec = 3;
        $pointTime = time(); //时间锚点
        while (true) {
            $nowTime = time();
            if (($nowTime - $pointTime) > $maxWaitSec) {
                //阻塞时长大于最大值，返回创建失败
                return resultError('创建转账单据失败');
            }
            //间隔0.5秒，查询一次
            usleep(500000);
            //请求转账单据信息
            $billInfo = $wxTransfer->getTransferBillInfo($outBillNo);
            if (!$billInfo) {
                return resultError('创建转账单据失败');
            }
            if($billInfo['state'] == 'FAIL') {
                //单据创建失败
                SubsidyBillService::updateStatus($billId, 5, $billInfo['fail_reason']);
                return resultError('创建转账单据失败');
            }
            if($billInfo['state'] == 'WAIT_USER_CONFIRM') {
                //单据状态变更为待用户确认，可终止阻塞
                break;
            }
        }

        //返回单据信息
        return resultSuccess([
            'out_bill_no' => $outBillNo,
            'package_info' => $packageInfo,
        ], '创建转账单据成功');
    }

    /**
     * 用户转账回调
     * @param $msg
     * @return array
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \ReflectionException
     * @throws \Throwable
     */
    public static function userTransferCallback($msg)
    {
        $app = (new WxCoupon())->getApp();
        $serve = $app->getServer();
        $message = [];
        try {
            $message = $serve->getRequestMessage()->toArray();
        } catch (\Exception $e) {
        }
        $outBillNo = $message['out_bill_no'] ?? 0;
        //回调信息日志
        \DB::table('wx_subsidy_callback_msg_log')
            ->insert([
                'request_message' => json_encode($msg),
                'message'         => json_encode($message),
                'out_bill_no'     => $outBillNo,
            ]);
        if(!$outBillNo) {
            return resultError('参数有误');
        }
        //查询单据信息
        $billInfo = SubsidyBillService::getRow([
            'out_bill_no' => $outBillNo,
        ]);
        if(!$billInfo) {
            return resultError('数据有误');
        }
        //更新单据状态
        $reason = $message['fail_reason'] ?? '';
        $billStatus = SubsidyBillService::convertWxStateToSubsidyBillStatus($message['state']);
        SubsidyBillService::updateStatus($billInfo['id'], $billStatus, $reason);
        if($billStatus == 4) { //转账成功，更新领取人领取状态
            ReceiverService::updateReceiveStatus($billInfo['receiver_id'], 3, Carbon::now()->toDateTimeString());
        }

        return resultSuccess();
    }

}
